package com.ljm.boot.redisson.util;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.ReactiveStringRedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.scripting.ScriptSource;
import org.springframework.scripting.support.ResourceScriptSource;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * @author Dominick Li
 * @describe RedisScriptUtils 执行lua脚本操作redis的工具类  lua脚本能保证多条指令的原子性
 * @date 2024/8/28
 */
@Slf4j
public class RedisScriptUtil {

    private static final RedisScript<Boolean> REPEAT_SUBMIT_LIMIT_SCRIPT;

    static {
        ScriptSource scriptSource = new ResourceScriptSource(new ClassPathResource("scripts/repeat_submit.lua"));
        String str = null;
        try {
            str = scriptSource.getScriptAsString();
        } catch (IOException e) {
            log.error("读取[repeat_submit.lua]脚本失败，{}", e.getMessage());
        }
        assert str != null;
        REPEAT_SUBMIT_LIMIT_SCRIPT = RedisScript.of(str, Boolean.class);
        log.info("init REPEAT_SUBMIT_LIMIT_SCRIPT success");
    }

    /**
     * 是否可以提交数据
     *
     * @param submitKey  防重复提交的key
     * @param expireTime 过期时间 单位秒
     * @return
     */
    public static Boolean isAllowed(String submitKey, Long expireTime) {
        Flux<Boolean> flux = getRedisTemplate().execute(REPEAT_SUBMIT_LIMIT_SCRIPT, Collections.singletonList(submitKey),
            Arrays.asList("", expireTime.toString()));
        Mono<Boolean> result = flux.onErrorResume(throwable -> {
            log.error("Error calling repeat_submit lua:{}", throwable.getMessage());
            return Flux.just(false);
        }).reduce((func, data) -> data);
        boolean flag=result.blockOptional().get();
        log.debug("isAllowed submitKey={},ttl={},flag={}",submitKey,expireTime,flag);
        return flag;
    }

    private static ReactiveStringRedisTemplate getRedisTemplate() {
        return Lazy.REDIS_TEMPLATE;
    }

    /**
     * 使用懒加载方式实例化ReactiveStringRedisTemplate()客户端工具
     */
    private static class Lazy {
        private static final ReactiveStringRedisTemplate REDIS_TEMPLATE = SpringUtil.getBean(ReactiveStringRedisTemplate.class);
    }

}
